package com.vay.de.dang.online.util;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Handler;
import android.os.Looper;
import android.view.Surface;
import android.view.SurfaceHolder;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

public class CameraHelper {

    private static int mCameraID = Camera.CameraInfo.CAMERA_FACING_FRONT;
    private static int mFps = 30;
    private static int mDisplayWidth;
    private static int mDisplayHeight;
    private static Camera mCamera;
    private static int mCameraPreviewFps;
    private static int mOrientation = 0;
    private static Camera.Size mPreviewSize;
    private static Camera.Size mPictureSize;
    private static Camera.PreviewCallback mPreviewCallback;
    private static OnPreviewCallback mCallback;
    private static boolean isAutoFocusing;
    private static Handler mHandler;

    // 打开相机，默认打开前置相机
    public static boolean openFrontalCamera(int displayWidth, int displayHeight, int fps) {
        try {
            if (mCamera != null) {
                releaseCamera();
            }

            Camera.CameraInfo info = new Camera.CameraInfo();
            int numCameras = Camera.getNumberOfCameras();
            for (int i = 0; i < numCameras; i++) {
                Camera.getCameraInfo(i, info);
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    mCamera = Camera.open(i);
                    mCameraID = info.facing;
                    break;
                }
            }

            // 如果没有前置摄像头，则打开默认的后置摄像头
            if (mCamera == null) {
                mCamera = Camera.open();
                mCameraID = Camera.CameraInfo.CAMERA_FACING_BACK;
            }

            // 没有摄像头时，抛出异常
            if (mCamera == null) {
                throw new RuntimeException("Unable to open camera");
            }

            mFps = fps;
            mDisplayWidth = displayWidth;
            mDisplayHeight = displayHeight;

            Camera.Parameters parameters = mCamera.getParameters();
            mCameraPreviewFps = chooseFixedPreviewFps(parameters, fps * 1000);
            parameters.setRecordingHint(true);
            mCamera.setParameters(parameters);
            mCamera.enableShutterSound(true);
            setPreviewSize(mCamera, displayWidth, displayHeight);
            setPictureSize(mCamera, displayWidth, displayHeight);
            mCamera.setDisplayOrientation(mOrientation);
            mHandler = new Handler(Looper.getMainLooper());
            mPreviewCallback = (data, camera) -> {
                if (mCallback != null) {
                    mCallback.onPreviewFrame(data, mPreviewSize.width, mPreviewSize.height);
                }
            };
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    // 打开指定相机
    public static boolean openCamera(int cameraID, int displayWidth, int displayHeight, int fps) {
        try {

            if (mCamera != null) {
                releaseCamera();
            }

            mCamera = Camera.open(cameraID);
            if (mCamera == null) {
                throw new RuntimeException("Unable to open camera");
            }

            mCameraID = cameraID;
            mFps = fps;
            mDisplayWidth = displayWidth;
            mDisplayHeight = displayHeight;

            Camera.Parameters parameters = mCamera.getParameters();
            mCameraPreviewFps = chooseFixedPreviewFps(parameters, fps * 1000);
            parameters.setRecordingHint(true);
            mCamera.setParameters(parameters);
            setPreviewSize(mCamera, displayWidth, displayHeight);
            setPictureSize(mCamera, displayWidth, displayHeight);
            mCamera.setDisplayOrientation(mOrientation);
            mCamera.enableShutterSound(true);
            mHandler = new Handler(Looper.getMainLooper());
            mPreviewCallback = (data, camera) -> {
                if (mCallback != null) {
                    mCallback.onPreviewFrame(data, mPreviewSize.width, mPreviewSize.height);
                }
            };
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    // 开始预览
    public static void startPreviewDisplay(SurfaceHolder holder) {

        if (mCamera == null || holder == null) return;

        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.setPreviewCallback(mCallback != null ? mPreviewCallback : null);
            mCamera.startPreview();
            mCamera.cancelAutoFocus();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 切换相机
    public static void switchCamera(int cameraID, SurfaceHolder holder) {

        if (mCameraID == cameraID) {
            return;
        }

        mCameraID = cameraID;
        // 释放原来的相机
        releaseCamera();
        // 打开相机
        openCamera(cameraID, mDisplayWidth, mDisplayHeight, mFps);
        // 打开预览
        startPreviewDisplay(holder);
    }

    public static void requestFocus() {
        if (mCamera == null || isAutoFocusing) return;
        try {
            mCamera.autoFocus((success, camera) -> {
                if (success) {
                    System.out.println("NS----->自动对焦成功");
                }
                mHandler.post(cancelFocus);
            });
            isAutoFocusing = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void startPreview() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(mCallback != null ? mPreviewCallback : null);
            mCamera.startPreview();
            mCamera.cancelAutoFocus();
            isAutoFocusing = false;
        }
    }

    public static void releaseCamera() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
        }
        mHandler = null;
        mCamera = null;
        isAutoFocusing = false;
    }

    public static void stopPreview() {
        if (mCamera != null) {
            mCamera.stopPreview();
        }
        isAutoFocusing = false;
    }

    // 拍照
    public static void takePicture(OnTakeImageCallback callback) {
        if (mCamera != null) {
            mCamera.takePicture(() -> {
                System.out.println("TakeImage");
            }, null, (data, camera) -> {
                try {
                    if (callback != null) {
                        callback.onCallbackImage(BitmapFactory.decodeByteArray(data, 0, data.length));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    // 设置预览大小
    private static void setPreviewSize(Camera camera, int expectWidth, int expectHeight) {
        Camera.Parameters parameters = camera.getParameters();
        mPreviewSize = calculateOptimalSize(parameters.getSupportedPreviewSizes(),
                expectWidth, expectHeight);
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        camera.setParameters(parameters);
    }

    // 设置拍摄的照片大小
    private static void setPictureSize(Camera camera, int expectWidth, int expectHeight) {
        Camera.Parameters parameters = camera.getParameters();
        mPictureSize = calculateOptimalSize(parameters.getSupportedPictureSizes(),
                expectWidth, expectHeight);
        parameters.setPictureSize(mPictureSize.width, mPictureSize.height);
        camera.setParameters(parameters);
    }

    // 计算最优尺寸
    public static Camera.Size calculateOptimalSize(List<Camera.Size> sizes, int expectWidth,
                                                   int expectHeight) {
        sortList(sizes);
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) expectWidth / expectHeight;
        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = expectHeight;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                minDiff = Math.abs(size.height - targetHeight);
                optimalSize = size;
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    minDiff = Math.abs(size.height - targetHeight);
                    optimalSize = size;
                }
            }
        }

        return optimalSize;
    }

    private static void sortList(List<Camera.Size> list) {
        Collections.sort(list, (pre, after) -> {
            if (pre.width > after.width) {
                return 1;
            } else if (pre.width < after.width) {
                return -1;
            }
            return 0;
        });
    }

    // 选择合适的FPS
    private static int chooseFixedPreviewFps(Camera.Parameters parameters, int expectedThoudandFps) {
        List<int[]> supportedFps = parameters.getSupportedPreviewFpsRange();
        for (int[] entry : supportedFps) {
            if (entry[0] == entry[1] && entry[0] == expectedThoudandFps) {
                parameters.setPreviewFpsRange(entry[0], entry[1]);
                return entry[0];
            }
        }
        int[] temp = new int[2];
        int guess;
        parameters.getPreviewFpsRange(temp);
        if (temp[0] == temp[1]) {
            guess = temp[0];
        } else {
            guess = temp[1] / 2;
        }
        return guess;
    }

    /**
     * 设置预览角度，setDisplayOrientation本身只能改变预览的角度
     * previewFrameCallback以及拍摄出来的照片是不会发生改变的，拍摄出来的照片角度依旧不正常的
     * 拍摄的照片需要自行处理
     * 这里Nexus5X的相机简直没法吐槽，后置摄像头倒置了，切换摄像头之后就出现问题了。
     *
     * @param activity
     */
    public static int calculateCameraPreviewOrientation(Activity activity) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraID, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        mOrientation = result;
        return result;
    }

    // 得到预览大小
    public static Camera.Size getPreviewSize() {
        if (mCamera != null) {
            return mCamera.getParameters().getPreviewSize();
        }
        return null;
    }

    // 拍照图片大小
    public static Camera.Size getPictureSize() {
        if (mCamera != null) {
            return mCamera.getParameters().getPictureSize();
        }
        return null;
    }

    // 计算变换矩阵
    public static Matrix configureTransform(int previewWidth, int previewHeight,
                                            int viewWidth, int viewHeight) {

        // 计算出将相机的尺寸 => View 的尺寸需要的缩放倍数
        float ratioPreview = (float) previewWidth / previewHeight;
        float ratioView = (float) viewWidth / viewHeight;
        float scaleX, scaleY;
        if (ratioView < ratioPreview) {
            scaleX = ratioPreview / ratioView;
            scaleY = 1;
        } else {
            scaleX = 1;
            scaleY = ratioView / ratioPreview;
        }

        // 计算出 View 的偏移量
        float scaledWidth = viewWidth * scaleX;
        float scaledHeight = viewHeight * scaleY;
        float dx = (viewWidth - scaledWidth) / 2;
        float dy = (viewHeight - scaledHeight) / 2;

        Matrix matrix = new Matrix();
        matrix.postScale(scaleX, scaleY);
        matrix.postTranslate(dx, dy);

        return matrix;
    }

    public static void setOnPreviewCallback(OnPreviewCallback callback) {
        mCallback = callback;
    }

    public interface OnTakeImageCallback {

        void onCallbackImage(Bitmap image);
    }

    public interface OnPreviewCallback {

        void onPreviewFrame(byte[] data, int width, int height);
    }

    private static Runnable cancelFocus = () -> {
        if (mCamera != null) {
            mCamera.cancelAutoFocus();
        }
        isAutoFocusing = false;
    };
}
